/*
  File:SoundflowerDevice.cpp

  Version:1.0.1
    ma++ ingalls  |  cycling '74  |  Copyright (C) 2004  |  soundflower.com
    
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

/*
    Soundflower is derived from Apple's 'PhantomAudioDriver'
    sample code.  It uses the same timer mechanism to simulate a hardware
    interrupt, with some additional code to compensate for the software
    timer's inconsistencies.  
    
    Soundflower basically copies the mixbuffer and presents it to clients
    as an input buffer, allowing a applications to send audio one another.
*/

#include "SoundflowerDevice.h"

#include "SoundflowerEngine.h"

#include <IOKit/audio/IOAudioControl.h>
#include <IOKit/audio/IOAudioLevelControl.h>
#include <IOKit/audio/IOAudioToggleControl.h>
#include <IOKit/audio/IOAudioDefines.h>

#include <IOKit/IOLib.h>

#define super IOAudioDevice

OSDefineMetaClassAndStructors(SoundflowerDevice, IOAudioDevice)

const SInt32 SoundflowerDevice::kVolumeMax = 65535;
const SInt32 SoundflowerDevice::kGainMax = 65535;

/*
 * initHardware()
 */

bool SoundflowerDevice::initHardware(IOService *provider)
{
    bool result = false;
    
  //  IOLog("SoundflowerDevice[%p]::initHardware(%p)\n", this, provider);
    
    if (!super::initHardware(provider)) {
        goto Done;
    }
    
    
    setDeviceName("Soundflower");
    setDeviceShortName("Soundflower");
    setManufacturerName("ma++ ingalls for Cycling '74");
    
    if (!createAudioEngines()) {
        goto Done;
    }
    
    result = true;
    
Done:

    return result;
}

/*
 * createAudioEngines()
 */

bool SoundflowerDevice::createAudioEngines()
{
    OSArray *audioEngineArray = OSDynamicCast(OSArray, getProperty(AUDIO_ENGINES_KEY));
    
    if (!audioEngineArray) {
        IOLog("SoundflowerDevice[%p]::createAudioEngine() - Error: no AudioEngine array in personality.\n", this);
        return false;
    }
    
    OSCollectionIterator *audioEngineIterator = OSCollectionIterator::withCollection(audioEngineArray);
    
    if (!audioEngineIterator)
        return true;
    
    OSDictionary *audioEngineDict;
    while (audioEngineDict = (OSDictionary *)audioEngineIterator->getNextObject()) {
        if (OSDynamicCast(OSDictionary, audioEngineDict) == NULL)
            continue;
        
        SoundflowerEngine *audioEngine = new SoundflowerEngine;
        if (! audioEngine) continue;
        
        if (! audioEngine->init(audioEngineDict)) continue;
        
        initControls(audioEngine);
        activateAudioEngine(audioEngine);

        audioEngine->release();
    }
    
    audioEngineIterator->release();
    
    
    
    
    return true;
}

#define addControl(control, handler) \
    if (!control) return false; \
    control->setValueChangeHandler(handler, this); \
    audioEngine->addDefaultAudioControl(control); \
    control->release();

bool SoundflowerDevice::initControls(Soundflower_AudioEngine *audioEngine)
{
    IOAudioControl *control = NULL;
//    OSNumber *channelCountNum = OSDynamicCast(OSNumber, audioEngine->getProperty(NUM_STREAMS_KEY)); // maximum 15, I assume
//    UInt32 channelCount = channelCountNum ? channelCountNum->unsigned32BitValue() + 1 : 2; // adding one for the 'all' channel
    
    for(UInt32 channel = 0; channel <= 16; channel++) {
        mVolume[channel] = mGain[channel] = 65535;
        mMuteOut[channel] = mMuteIn[channel] = false;
    }
    
    char *channelNameMap[17] = {kIOAudioControlChannelNameAll,
                                kIOAudioControlChannelNameLeft,
                                kIOAudioControlChannelNameRight,
                                kIOAudioControlChannelNameCenter,
                                kIOAudioControlChannelNameLeftRear,
                                kIOAudioControlChannelNameRightRear,
                                kIOAudioControlChannelNameSub};
    for(UInt32 channel = 7; channel <= 16; channel++)
        channelNameMap[channel] = "Unknown Channel";
    
    for(unsigned channel = 0; channel <= 16; channel++) {
        // Create a  output volume control for each channel with an int range from 0 to 65535
        // and a db range from -22.5 to 0.0
        // Once each control is added to the audio engine, they should be released
        // so that when the audio engine is done with them, they get freed properly
        control = IOAudioLevelControl::createVolumeControl(SoundflowerDevice::kVolumeMax,	// Initial value
                                                           0,		// min value
                                                           SoundflowerDevice::kVolumeMax,	// max value
                                                           (-22 << 16) + (32768),	// -22.5 in IOFixed (16.16)
                                                           0,		// max 0.0 in IOFixed
                                                           channel, //kIOAudioControlChannelIDDefaultLeft,
                                                           channelNameMap[channel], //kIOAudioControlChannelNameLeft,
                                                           channel,		// control ID - driver-defined
                                                           kIOAudioControlUsageOutput);
        addControl(control, (IOAudioControl::IntValueChangeHandler)volumeChangeHandler);
        
        // Gain control for each channel
        control = IOAudioLevelControl::createVolumeControl(SoundflowerDevice::kGainMax,	// Initial value
                                                           0,		// min value
                                                           SoundflowerDevice::kGainMax,	// max value
                                                           0,		// min 0.0 in IOFixed
                                                           (22 << 16) + (32768),	// 22.5 in IOFixed (16.16)
                                                           channel, //kIOAudioControlChannelIDDefaultLeft,
                                                           channelNameMap[channel], //kIOAudioControlChannelNameLeft,
                                                           channel,		// control ID - driver-defined
                                                           kIOAudioControlUsageInput);
        addControl(control, (IOAudioControl::IntValueChangeHandler)gainChangeHandler);
    }
    
    
    // Create an output mute control
    control = IOAudioToggleControl::createMuteControl(false,	// initial state - unmuted
                                                      kIOAudioControlChannelIDAll,	// Affects all channels
                                                      kIOAudioControlChannelNameAll,
                                                      0,		// control ID - driver-defined
                                                      kIOAudioControlUsageOutput);
    addControl(control, (IOAudioControl::IntValueChangeHandler)outputMuteChangeHandler);
    
    
    // Create an input mute control
    control = IOAudioToggleControl::createMuteControl(false,	// initial state - unmuted
                                                      kIOAudioControlChannelIDAll,	// Affects all channels
                                                      kIOAudioControlChannelNameAll,
                                                      0,		// control ID - driver-defined
                                                      kIOAudioControlUsageInput);
    addControl(control, (IOAudioControl::IntValueChangeHandler)inputMuteChangeHandler);
    
    
    return true;
}

/*
 * volumeChangeHandler()
 */
 
IOReturn SoundflowerDevice::volumeChangeHandler(IOService *target, IOAudioControl *volumeControl, SInt32 oldValue, SInt32 newValue)
{
    IOReturn result = kIOReturnBadArgument;
    SoundflowerDevice *audioDevice;
    
    audioDevice = (SoundflowerDevice *)target;
    if (audioDevice) {
        result = audioDevice->volumeChanged(volumeControl, oldValue, newValue);
    }
    
    return result;
}

/*
 * volumeChanged()
 */
 
IOReturn SoundflowerDevice::volumeChanged(IOAudioControl *volumeControl, SInt32 oldValue, SInt32 newValue)
{
    //IOLog("SoundflowerDevice[%p]::volumeChanged(%p, %ld, %ld)\n", this, volumeControl, oldValue, newValue);
    
    if (volumeControl) {
        //IOLog("\t-> Channel %ld\n", volumeControl->getChannelID());
         mVolume[volumeControl->getChannelID()] = newValue;
    }

    return kIOReturnSuccess;
}

/*
 * outputMuteChangeHandler()
 */
 
IOReturn SoundflowerDevice::outputMuteChangeHandler(IOService *target, IOAudioControl *muteControl, SInt32 oldValue, SInt32 newValue)
{
    IOReturn result = kIOReturnBadArgument;
    SoundflowerDevice *audioDevice;
    
    audioDevice = (SoundflowerDevice *)target;
    if (audioDevice) {
        result = audioDevice->outputMuteChanged(muteControl, oldValue, newValue);
    }
    
    return result;
}

/*
 * outputMuteChanged()
 */
 
IOReturn SoundflowerDevice::outputMuteChanged(IOAudioControl *muteControl, SInt32 oldValue, SInt32 newValue)
{
    //IOLog("SoundflowerDevice[%p]::outputMuteChanged(%p, %ld, %ld)\n", this, muteControl, oldValue, newValue);
    
    if (muteControl) {
        //IOLog("\t-> Channel %ld\n", muteControl->getChannelID());
         mMuteOut[muteControl->getChannelID()] = newValue;
    }
        
    return kIOReturnSuccess;
}

/*
 * gainChangeHandler()
 */
 
IOReturn SoundflowerDevice::gainChangeHandler(IOService *target, IOAudioControl *gainControl, SInt32 oldValue, SInt32 newValue)
{
    IOReturn result = kIOReturnBadArgument;
    SoundflowerDevice *audioDevice;
    
    audioDevice = (SoundflowerDevice *)target;
    if (audioDevice) {
        result = audioDevice->gainChanged(gainControl, oldValue, newValue);
    }
    
    return result;
}

/*
 * gainChanged()
 */
 
IOReturn SoundflowerDevice::gainChanged(IOAudioControl *gainControl, SInt32 oldValue, SInt32 newValue)
{
    //IOLog("SoundflowerDevice[%p]::gainChanged(%p, %ld, %ld)\n", this, gainControl, oldValue, newValue);
    
    if (gainControl) {
        //IOLog("\t-> Channel %ld\n", gainControl->getChannelID());
         mGain[gainControl->getChannelID()] = newValue;
    }
    
    return kIOReturnSuccess;
}

/*
 * inputMuteChangeHandler()
 */
 
IOReturn SoundflowerDevice::inputMuteChangeHandler(IOService *target, IOAudioControl *muteControl, SInt32 oldValue, SInt32 newValue)
{
    IOReturn result = kIOReturnBadArgument;
    SoundflowerDevice *audioDevice;
    
    audioDevice = (SoundflowerDevice *)target;
    if (audioDevice) {
        result = audioDevice->inputMuteChanged(muteControl, oldValue, newValue);
    }
    
    return result;
}

/*
 * inputMuteChanged()
 */
 
IOReturn SoundflowerDevice::inputMuteChanged(IOAudioControl *muteControl, SInt32 oldValue, SInt32 newValue)
{
    //IOLog("SoundflowerDevice[%p]::inputMuteChanged(%p, %ld, %ld)\n", this, muteControl, oldValue, newValue);
    
    if (muteControl) {
        //IOLog("\t-> Channel %ld\n", muteControl->getChannelID());
         mMuteIn[muteControl->getChannelID()] = newValue;
    }
        
    return kIOReturnSuccess;
}
